<?php
namespace FastPHP\Library;

class Cache
{
    protected static $instance = [];
    protected $handler;
    protected $options = [];
    protected $tag = null;

    public static function store($name = null)
    {
        $config = require ROOT_PATH . 'config/cache.php';
        $name = $name ?: $config['default'];
        if (!isset(self::$instance[$name])) {
            $store = $config['stores'][$name] ?? $config['stores'][$config['default']];
            switch ($store['type']) {
                case 'file':
                    self::$instance[$name] = new FileCache($store);
                    break;
                case 'memcache':
                    // TODO: 实现 MemcacheCache
                    throw new \Exception('Memcache 驱动未实现');
                case 'redis':
                    // TODO: 实现 RedisCache
                    throw new \Exception('Redis 驱动未实现');
                case 'sqlite':
                    // TODO: 实现 SqliteCache
                    throw new \Exception('Sqlite 驱动未实现');
                case 'xcache':
                    // TODO: 实现 XCacheCache
                    throw new \Exception('XCache 驱动未实现');
                case 'wincache':
                    // TODO: 实现 WinCacheCache
                    throw new \Exception('WinCache 驱动未实现');
                default:
                    throw new \Exception('不支持的缓存驱动: ' . $store['type']);
            }
        }
        return self::$instance[$name];
    }

    // 快捷静态方法
    public static function get($key, $default = null, $store = null) { return self::store($store)->get($key, $default); }
    public static function set($key, $value, $expire = null, $store = null) { return self::store($store)->set($key, $value, $expire); }
    public static function delete($key, $store = null) { return self::store($store)->delete($key); }
    public static function clear($tag = null, $store = null) { return self::store($store)->clear($tag); }
    public static function inc($key, $step = 1, $store = null) { return self::store($store)->inc($key, $step); }
    public static function dec($key, $step = 1, $store = null) { return self::store($store)->dec($key, $step); }
    public static function tag($tag, $store = null) { return self::store($store)->tag($tag); }
}

class FileCache
{
    protected $options;
    protected $tag = null;

    public function __construct($options)
    {
        $this->options = $options;
        if (!is_dir($this->options['path'])) {
            mkdir($this->options['path'], 0777, true);
        }
    }

    protected function filename($key)
    {
        $prefix = $this->options['prefix'] ?? '';
        return $this->options['path'] . $prefix . md5($key) . '.php';
    }

    public function get($key, $default = null)
    {
        $file = $this->filename($key);
        if (!is_file($file)) return $default;
        $data = include $file;
        if (!is_array($data) || ($data['expire'] > 0 && $data['expire'] < time())) {
            @unlink($file);
            return $default;
        }
        return $data['value'];
    }

    public function set($key, $value, $expire = null)
    {
        $expire = is_null($expire) ? ($this->options['expire'] ?? 3600) : $expire;
        $file = $this->filename($key);
        $data = [
            'expire' => $expire > 0 ? (time() + $expire) : 0,
            'value'  => $value,
            'tag'    => $this->tag,
        ];
        $str = '<?php return ' . var_export($data, true) . ';';
        file_put_contents($file, $str);
        if ($this->tag) $this->setTagItem($this->tag, $file);
        return true;
    }

    public function delete($key)
    {
        $file = $this->filename($key);
        return is_file($file) ? @unlink($file) : true;
    }

    public function clear($tag = null)
    {
        $path = $this->options['path'];
        if ($tag) {
            $tagFile = $this->tagFilename($tag);
            if (is_file($tagFile)) {
                $files = include $tagFile;
                foreach ($files as $f) @unlink($f);
                @unlink($tagFile);
            }
        } else {
            foreach (glob($path . '*.php') as $file) @unlink($file);
            foreach (glob($path . '*.tag') as $file) @unlink($file);
        }
        return true;
    }

    public function inc($key, $step = 1)
    {
        $val = $this->get($key, 0) + $step;
        $this->set($key, $val);
        return $val;
    }

    public function dec($key, $step = 1)
    {
        $val = $this->get($key, 0) - $step;
        $this->set($key, $val);
        return $val;
    }

    public function tag($tag)
    {
        $this->tag = $tag;
        return $this;
    }

    protected function tagFilename($tag)
    {
        return $this->options['path'] . 'tag_' . md5($tag) . '.tag';
    }

    protected function setTagItem($tag, $file)
    {
        $tagFile = $this->tagFilename($tag);
        $files = is_file($tagFile) ? include $tagFile : [];
        if (!in_array($file, $files)) $files[] = $file;
        $str = '<?php return ' . var_export($files, true) . ';';
        file_put_contents($tagFile, $str);
    }
} 